/*!
 * \file EventNotifier.cpp
 * \project WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 事件通知器实现文件
 * 
 * \details 本文件实现了EventNotifier类的各种方法，用于在WonderTrader回测框架中处理事件通知和数据广播。
 *          主要实现功能包括：
 *          - 动态加载消息队列模块
 *          - 初始化消息发布服务器
 *          - 发布各种类型的事件通知
 *          - 发布数据和资金变化信息
 *          - 管理消息队列的生命周期
 */
#include "EventNotifier.h"
#include "WtHelper.h"

#include "../Share/TimeUtils.hpp"
#include "../Share/DLLHelper.hpp"

#include "../Includes/WTSVariant.hpp"
#include "../WTSTools/WTSLogger.h"

#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
namespace rj = rapidjson;

USING_NS_WTP;

/*!
 * \brief 消息队列日志回调函数
 * 
 * \param id 消息队列服务器ID
 * \param message 日志消息内容
 * \param bServer 是否为服务器日志
 * 
 * \details 用于处理消息队列模块产生的日志信息。该函数作为回调接口，
 *          接收来自消息队列模块的日志信息。当前实现为空，可以根据需要
 *          添加具体的日志处理逻辑。
 */
void on_mq_log(unsigned long id, const char* message, bool bServer)
{

}

/*!
 * \brief EventNotifier构造函数
 * 
 * \details 初始化事件通知器对象，设置消息队列服务器ID为0。
 *          构造函数只进行基本的成员变量初始化，实际的初始化工作
 *          由init()函数完成。
 */
EventNotifier::EventNotifier()
	: _mq_sid(0)
{
	
}


/*!
 * \brief EventNotifier析构函数
 * 
 * \details 清理事件通知器资源，包括销毁消息队列服务器。
 *          如果消息队列服务器已创建且销毁函数可用，则调用
 *          销毁函数清理服务器资源。
 */
EventNotifier::~EventNotifier()
{
	// 如果消息队列服务器已创建且销毁函数可用，则销毁服务器
	if (_remover && _mq_sid != 0)
		_remover(_mq_sid);
}

/*!
 * \brief 初始化事件通知器
 * 
 * \param cfg 配置参数
 * \return 初始化是否成功
 * 
 * \details 根据配置参数初始化事件通知器。主要完成以下工作：
 *          1. 检查配置中是否启用事件通知功能
 *          2. 动态加载消息队列模块
 *          3. 获取消息队列相关函数接口
 *          4. 注册日志回调函数
 *          5. 创建消息队列服务器实例
 *          
 *          模块加载逻辑：
 *          - 首先在当前工作目录查找WtMsgQue模块
 *          - 如果不存在，则在实例目录中查找
 *          - 加载成功后获取必要的函数接口
 *          
 *          错误处理：
 *          - 如果active配置为false，直接返回false
 *          - 模块加载失败或接口不兼容时返回false
 */
bool EventNotifier::init(WTSVariant* cfg)
{
	// 检查配置中是否启用事件通知功能
	if (!cfg->getBoolean("active"))
		return false;

	// 获取消息队列URL配置
	m_strURL = cfg->getCString("url");
	std::string module = DLLHelper::wrap_module("WtMsgQue", "lib");
	
	// 首先检查当前目录是否有对应模块
	std::string dllpath = WtHelper::getCWD() + module;
	// 如果没有，再次检查模块目录，与dll同目录的
	if (!StdFile::exists(dllpath.c_str()))
		dllpath = WtHelper::getInstDir() + module;

	// 动态加载消息队列模块
	DllHandle dllInst = DLLHelper::load_library(dllpath.c_str());
	if (dllInst == NULL)
	{
		WTSLogger::error("MQ module %s loading failed", dllpath.c_str());
		return false;
	}

	// 获取消息队列服务器创建函数
	_creator = (FuncCreateMQServer)DLLHelper::get_symbol(dllInst, "create_server");
	if (_creator == NULL)
	{
		DLLHelper::free_library(dllInst);
		WTSLogger::error("MQ module %s is not compatible", dllpath.c_str());
		return false;
	}

	// 获取其他必要的函数接口
	_remover = (FuncDestroyMQServer)DLLHelper::get_symbol(dllInst, "destroy_server");
	_publisher = (FundPublishMessage)DLLHelper::get_symbol(dllInst, "publish_message");
	_register = (FuncRegCallbacks)DLLHelper::get_symbol(dllInst, "regiter_callbacks");

	// 注册回调函数
	_register(on_mq_log);
	
	// 创建一个MQServer实例
	_mq_sid = _creator(m_strURL.c_str(), true);

	WTSLogger::info("EventNotifier initialized with channel %s", m_strURL.c_str());

	return true;
}

/*!
 * \brief 发布事件通知
 * 
 * \param evtType 事件类型字符串
 * 
 * \details 向消息队列发布事件通知。事件将被发布到"BT_EVENT"主题下，
 *          供订阅者接收和处理。这种机制允许回测系统的不同组件之间
 *          进行松耦合的事件通信。
 *          
 *          功能特点：
 *          - 使用固定的"BT_EVENT"主题
 *          - 自动计算消息长度
 *          - 线程安全的消息发布
 *          
 *          注意事项：
 *          - 只有在消息发布器可用时才发送消息
 *          - 消息内容为事件类型的字符串表示
 */
void EventNotifier::notifyEvent(const char* evtType)
{
	if (_publisher)
		_publisher(_mq_sid, "BT_EVENT", evtType, strlen(evtType));
}

/*!
 * \brief 发布数据通知
 * 
 * \param topic 消息主题
 * \param data 数据指针
 * \param dataLen 数据长度
 * 
 * \details 向指定主题发布数据。该函数提供了一个通用的数据发布接口，
 *          可以向任意主题发布任意类型的二进制数据。这使得系统可以
 *          灵活地传递各种类型的数据，如市场数据、交易信号等。
 *          
 *          功能特点：
 *          - 支持自定义消息主题
 *          - 支持任意类型的二进制数据
 *          - 精确的数据长度控制
 *          - 高效的数据传输
 *          
 *          使用场景：
 *          - 发布市场数据快照
 *          - 传递交易信号
 *          - 广播策略状态信息
 *          - 分发计算结果数据
 */
void EventNotifier::notifyData(const char* topic, void* data , uint32_t dataLen)
{
	if (_publisher)
		_publisher(_mq_sid, topic, (const char*)data, dataLen);
}

/*!
 * \brief 发布资金变化通知
 * 
 * \param topic 消息主题
 * \param uDate 日期（格式：YYYYMMDD）
 * \param total_profit 总盈亏
 * \param dynprofit 动态盈亏
 * \param dynbalance 动态余额
 * \param total_fee 总手续费
 * 
 * \details 发布包含资金变化信息的JSON格式消息。该函数将多个资金相关
 *          的数值参数组合成一个结构化的JSON对象，然后发布到指定主题。
 *          这种格式化的消息便于订阅者解析和处理资金变化信息。
 *          
 *          JSON结构：
 *          - date: 交易日期
 *          - total_profit: 累计总盈亏
 *          - dynprofit: 当前动态盈亏
 *          - dynbalance: 当前动态账户余额
 *          - total_fee: 累计总手续费
 *          
 *          使用场景：
 *          - 回测过程中的资金变化通知
 *          - 实时交易的账户状态更新
 *          - 风险监控系统的资金预警
 *          - 策略性能分析的数据收集
 *          
 *          技术特点：
 *          - 使用RapidJSON生成格式化JSON
 *          - 自动处理内存管理和字符串转换
 *          - 高性能的JSON序列化
 */
void EventNotifier::notifyFund(const char* topic, uint32_t uDate, double total_profit, double dynprofit, double dynbalance, double total_fee)
{
	std::string output;
	{
		// 创建JSON文档对象
		rj::Document root(rj::kObjectType);
		rj::Document::AllocatorType &allocator = root.GetAllocator();

		// 添加资金相关的各项数据到JSON对象
		root.AddMember("date", uDate, allocator);
		root.AddMember("total_profit", total_profit, allocator);
		root.AddMember("dynprofit", dynprofit, allocator);
		root.AddMember("dynbalance", dynbalance, allocator);
		root.AddMember("total_fee", total_fee, allocator);

		// 将JSON对象序列化为格式化的字符串
		rj::StringBuffer sb;
		rj::PrettyWriter<rj::StringBuffer> writer(sb);
		root.Accept(writer);

		output = sb.GetString();
	}

	// 发布JSON格式的资金变化消息
	if (_publisher)
		_publisher(_mq_sid, topic, (const char*)output.c_str(), output.size());
}
